/**
 * Copyright (C) 2013-2014 the original author or authors.
 */
package com.foreveross.bsl.system.application.impl.mobile;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.foreveross.bsl.auth.AuthUser;
import com.foreveross.bsl.auth.service.TicketApplication;
import com.foreveross.bsl.common.utils.bean.BeanHelper;
import com.foreveross.bsl.common.utils.context.ContextThead;
import com.foreveross.bsl.common.utils.enums.AccountStatus;
import com.foreveross.bsl.common.utils.enums.AuthType;
import com.foreveross.bsl.common.utils.enums.OperatEnum;
import com.foreveross.bsl.common.utils.enums.Protocol;
import com.foreveross.bsl.common.utils.exception.AppExcType;
import com.foreveross.bsl.common.utils.exception.ApplicationException;
import com.foreveross.bsl.mam.model.rbac.domain.User;
import com.foreveross.bsl.system.application.extend.AuthSettingApplication;
import com.foreveross.bsl.system.application.extend.AuthTypeApplication;
import com.foreveross.bsl.system.application.extend.vo.AuthInfoVO;
import com.foreveross.bsl.system.application.extend.vo.AuthResultVO;
import com.foreveross.bsl.system.application.extend.vo.AuthSettingVO;
import com.foreveross.bsl.system.application.impl.extend.AuthInstanceFactory;
import com.foreveross.bsl.system.application.mobile.AccountMobileApplication;
import com.foreveross.bsl.system.model.domain.Account;
import com.foreveross.bsl.system.model.domain.ApplicationRegister;

/**
 * @description
 * 
 * @author rening
 * @version v1.0
 * 
 * @2013-6-7
 * 
 */
@Named("accountMobileApplication")
public class AccountMobileApplicationImpl implements AccountMobileApplication {

	private static Logger logger = LoggerFactory
			.getLogger(AccountMobileApplicationImpl.class);

	@Inject
	TicketApplication ticketApplication;

	@Inject
	AuthSettingApplication authSettingApplication;

	// @Inject
	// MonitorApplication monitorApplication;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.foreveross.bsl.system.application.DatabaseInfoApplication#getEntity
	 * (java.lang.String)
	 */
	@Override
	public Map<String, Object> getEntity(String sessionKey) {
		Map<String, Object> result = new HashMap<String, Object>();
		AuthUser user;
		try {
			user = (AuthUser) ticketApplication.getEntity(sessionKey);
		} catch (ApplicationException e) {
			throw e;
		} catch (Exception e) {
			throw new ApplicationException(AppExcType.getSysCode(
					AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "001"),
					e.getMessage());
		}
		result.put("result", "true");
		result.put("authUser", user);
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.foreveross.bsl.system.application.AccountApplication#mobileLogin(
	 * java.lang.String)
	 */
	@Override
	public Map<String, Object> logout(String sessionKey) {
		Map<String, Object> result = new HashMap<String, Object>();
		try {
			ticketApplication.deleteEntity(sessionKey);
		} catch (ApplicationException e) {
			throw e;
		} catch (Exception e) {
			throw new ApplicationException(AppExcType.getSysCode(
					AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "001"),
					e.getMessage());
		}
		result.put("result", "true");
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.foreveross.bsl.system.mobile.application.AccountMobileApplication
	 * #mobileLogin(java.lang.String, java.lang.String, java.lang.String,
	 * java.lang.String, java.lang.String)
	 */
	@Override
	public Map<String, Object> mobileLogin(String appKey, String username,
			String password, String appId, String deviceId) {
		Map<String, Object> result = new HashMap<String, Object>();

		ApplicationRegister ar = new ApplicationRegister();
		List<ApplicationRegister> appList = null;
		try {
			appList = ar.findListByProperty("appKey", OperatEnum.EQ, appKey);
		} catch (ApplicationException e) {
			throw e;
		} catch (Exception e) {
			throw new ApplicationException(AppExcType.getSysCode(
					AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "001"),
					e.getMessage());
		}
		if (appList == null || appList.isEmpty()) {
			throw new ApplicationException(AppExcType.getBusCode(
					AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "001"),
					"application is not register or Wrong appKey! ");
		}

		Account acc = new Account();
		try {
			acc = (Account) acc.findOne(appList.get(0).getAccountId());
		} catch (ApplicationException e) {
			throw e;
		} catch (Exception e) {
			throw new ApplicationException(AppExcType.getSysCode(
					AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "001"),
					e.getMessage());
		}
		if (acc == null) {
			throw new ApplicationException(AppExcType.getBusCode(
					AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "002"),
					"application is not register or Wrong appKey! ");
		} else {
			AccountStatus as = AccountStatus.getByCode(acc.getStatus());
			switch (as) {
			case Suspended:
				throw new ApplicationException(AppExcType.getBusCode(
						AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "021"),
						"Account is suspended! ");
			case Disabled:
				throw new ApplicationException(AppExcType.getBusCode(
						AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "022"),
						"Account is disabled! ");
			case Inactive:
				throw new ApplicationException(AppExcType.getBusCode(
						AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "023"),
						"Account is inactive! ");
			case Auditing:
				throw new ApplicationException(AppExcType.getBusCode(
						AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "024"),
						"Account is auditing! ");
			default:
				break;
			}
		}

		// current User auth type
		String code = acc.getAuthType() != null ? acc.getAuthType()
				: AuthType.Default.getValue();
		AuthType authType = AuthType.getByCode(code);

		User user = null;
		AuthResultVO authResult = null;
		switch (authType) {
		case External:
			authResult = extendAuth(username, password, appKey, appId, acc);
			user = new User();
			user.setUsername(username);
			user.setStatus(AccountStatus.Normal.getValue());
			break;
		default:
			ContextThead.setThreadVar(acc.getUsername());
			user = systemAuth(username, password);
			break;
		}

		AuthUser authUser = new AuthUser();
		BeanHelper.copyProperties(authUser, user);
		authUser.setAccountId(acc.getId());
		authUser.setAccountName(acc.getName());
		authUser.setAccountUsername(acc.getUsername());
		String sessionKey = ticketApplication.generateTicket(authUser);
		result.put("result", "true");
		result.put("sessionKey", sessionKey);
		if (authResult != null) {
			result.put("userInfo", authResult.getMessage());
			result.put("setCookie", authResult.getSetCookie());
		}

		/*
		 * add by lizhifeng 注意必须要开启监控服务 try {
		 * ContextThead.setThreadSessionKey(sessionKey); MonitorVO monitorVO =
		 * new MonitorVO(); monitorVO.setAction("Login");
		 * monitorVO.setClassName(
		 * "com.foreveross.bsl.system.mobile.application.impl.AccountMobileApplicationImpl"
		 * ); monitorVO.setMethodName("mobileLogin");
		 * monitorVO.setAppName(appId);
		 * monitorVO.setUserName(user.getUsername()); ObjectMapper objectMapper
		 * = new ObjectMapper(); String monitorJson =
		 * objectMapper.writeValueAsString(monitorVO);
		 * monitorApplication.saveEntity(monitorJson); } catch (Exception e) {
		 * logger.error("登录监控插入日志失败，请检查是否开启监控服务" + e); }
		 */

		return result;
	}

	/**
	 * @param username
	 * @param password
	 * @param acc
	 * @param user
	 * @return
	 */
	private AuthResultVO extendAuth(String username, String password,
			String appKey, String appId, Account acc) {
		AuthInfoVO authInfo = new AuthInfoVO();
		authInfo.setUsername(username);
		authInfo.setPassword(password);
		authInfo.setAppKey(appKey);
		authInfo.setAppId(appId);

		AuthSettingVO authSetting = null;
		List<AuthSettingVO> authSettingList = authSettingApplication
				.getEntityByUserName(acc.getUsername());
		if (!authSettingList.isEmpty()) {
			for (AuthSettingVO authSettingVO : authSettingList) {
				if ("c".equals(authSettingVO.getClientType())) {
					authSetting = authSettingVO;
					break;
				}
			}
		}

		if (authSetting == null) {
			throw new ApplicationException(AppExcType.getBusCode(
					AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "031"),
					"auth setting not set ");
		}

		Protocol p = Protocol.getByCode(authSetting.getProtocol());
		AuthTypeApplication authTypeApp = AuthInstanceFactory
				.getAuthInstance(p);
		AuthResultVO authResult = authTypeApp.auth(authInfo, authSetting);
		User user = null;
		if (authResult.isResult()) {
			user = new User();
			user.setUsername(username);
			user.setStatus(AccountStatus.Normal.getValue());

			ContextThead.setThreadVar(acc.getUsername());
			List<User> list = null;
			list = user.findListByProperty(new String[] { "username" },
					new OperatEnum[] { OperatEnum.EQ },
					new String[] { username });
			if (list.isEmpty()) {
				user.save();
			}
		} else {
			throw new ApplicationException(AppExcType.getBusCode(
					AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "007"),
					"Username is not exist or Wrong password! ");
		}

		return authResult;
	}

	/**
	 * @param username
	 * @param result
	 * @param encodePassword
	 * @param acc
	 * @return
	 */

	private User systemAuth(String username, String password) {

		Md5PasswordEncoder md = new Md5PasswordEncoder();
		String encodePassword = md.encodePassword(password, username);

		User user = new User();
		List<User> list = null;
		try {
			list = user.findListByProperty(new String[] { "username",
					"password" }, new OperatEnum[] { OperatEnum.EQ,
					OperatEnum.EQ }, new String[] { username, encodePassword });
		} catch (ApplicationException e) {
			throw e;
		} catch (Exception e) {
			throw new ApplicationException(AppExcType.getSysCode(
					AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "001"),
					e.getMessage());
		}
		if (list != null && !list.isEmpty()) {

			user = list.get(0);
			AccountStatus as = AccountStatus.getByCode(user.getStatus());
			switch (as) {
			case Suspended:
				throw new ApplicationException(AppExcType.getBusCode(
						AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "003"),
						"user is suspended! ");
			case Disabled:
				throw new ApplicationException(AppExcType.getBusCode(
						AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "004"),
						"user is disabled! ");
			case Inactive:
				throw new ApplicationException(AppExcType.getBusCode(
						AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "005"),
						"user is inactive! ");
			case Auditing:
				throw new ApplicationException(AppExcType.getBusCode(
						AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "006"),
						"user is auditing! ");
			default:
				break;
			}

			return user;
		} else {
			throw new ApplicationException(AppExcType.getBusCode(
					AppExcType.APP_LAYER, AppExcType.SYS_MODUL, "007"),
					"Username is not exist or Wrong password! ");
		}
	}

}
